package com.wswy.wzcx.widget;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Rect;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.NestedScrollingParent;
import android.support.v4.view.NestedScrollingParentHelper;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.LinearLayoutCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.LinearInterpolator;
import android.widget.OverScroller;
import com.wswy.wzcx.widget.utils.ViewGroupUtils;

public class HomeStickyLayout extends LinearLayoutCompat implements NestedScrollingParent {


  private static final String TAG = "HomeStickyLayout";

  public HomeStickyLayout(Context context) {
    this(context, null);
  }

  public HomeStickyLayout(Context context,
      @Nullable AttributeSet attrs) {
    this(context, attrs, 0);
  }

  public HomeStickyLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    setOrientation(VERTICAL);
    mScroller = new OverScroller(context);

    mParentHelper =new NestedScrollingParentHelper(this);
  }


  private View topView;
  private View stickyView;
  private View scrollableView;


  private int mTopViewHeight;

  private OverScroller mScroller;
  private ValueAnimator mOffsetAnimator;

  private NestedScrollingParentHelper mParentHelper;


  @Override
  protected void onFinishInflate() {
    super.onFinishInflate();
    int childCount = getChildCount();
    for (int i = 0; i < childCount; i++) {
      View childAt = getChildAt(i);

      if (childAt.getVisibility() != View.GONE) {

        if (topView == null) {
          topView = childAt;

          continue;
        }

        if (stickyView == null) {
          stickyView = childAt;
          continue;
        }

        if (scrollableView == null){
          scrollableView = childAt;
          break;
        }

      }

    }

    if (topView == null || stickyView == null) {
      throw new NullPointerException("topView or stickyView must not be null !");
    }

  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    //topView.measure(widthMeasureSpec,MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));


    ViewGroup.LayoutParams params = scrollableView.getLayoutParams();
    params.height = getMeasuredHeight() - stickyView.getMeasuredHeight();
    setMeasuredDimension(getMeasuredWidth(), topView.getMeasuredHeight() + stickyView.getMeasuredHeight() + scrollableView.getMeasuredHeight());


  }

  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    mTopViewHeight = topView.getMeasuredHeight();
    
  }

  @Override
  public void scrollTo(int x, int y) {

    if (y < 0) {
      y = 0;
    }
    if (y > mTopViewHeight) {
      y = mTopViewHeight;
    }
    if (y != getScrollY()) {
      super.scrollTo(x, y);
    }
  }

  @Override
  public void computeScroll() {
    if (mScroller.computeScrollOffset()) {
      scrollTo(0, mScroller.getCurrY());
      invalidate();
    }
  }


  @Override
  public boolean onStartNestedScroll(@NonNull View child, @NonNull View target,
      int nestedScrollAxes) {
    return true;
  }

  @Override
  public void onNestedScrollAccepted(@NonNull View child, @NonNull View target,
      int nestedScrollAxes) {
    Log.e(TAG, "onNestedScrollAccepted");

    mParentHelper.onNestedScrollAccepted(child, target, nestedScrollAxes);

  }


  @Override
  public void onStopNestedScroll(@NonNull View target) {
    Log.e(TAG, "onStopNestedScroll");
    mParentHelper.onStopNestedScroll(target);
  }

  @Override
  public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed,
      int dyUnconsumed) {
    Log.e(TAG, "onNestedScroll");


    ViewCompat.startNestedScroll(this,mParentHelper.getNestedScrollAxes());
  }


  @Override
  public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed) {

    boolean hiddenTop = dy > 0 && getScrollY() < mTopViewHeight;
    boolean showTop = dy < 0 && getScrollY() >= 0 && !target.canScrollVertically(-1);

    Log.e(TAG, "onNestedPreScroll");

    if (hiddenTop || showTop) {
      scrollBy(0, dy);
      consumed[1] = dy;
    }
  }


  @Override
  public boolean onNestedFling(@NonNull View target, float velocityX, float velocityY,
      boolean consumed) {
    //如果是recyclerView 根据判断第一个元素是哪个位置可以判断是否消耗
    //这里判断如果第一个元素的位置是大于TOP_CHILD_FLING_THRESHOLD的
    //认为已经被消耗，在animateScroll里不会对velocityY<0时做处理

    if (!consumed) {
      animateScroll(velocityY, computeDuration(0), consumed);
    } else {
      animateScroll(velocityY, computeDuration(velocityY), consumed);
    }
    return true;
  }

  @Override
  public boolean onNestedPreFling(@NonNull View target, float velocityX, float velocityY) {
    //不做拦截 可以传递给子View
    return false;
  }

  @Override
  public int getNestedScrollAxes() {
    Log.e(TAG, "getNestedScrollAxes");
    return 0;
  }

  /**
   * 根据速度计算滚动动画持续时间
   */
  private int computeDuration(float velocityY) {
    final int distance;
    if (velocityY > 0) {
      distance = Math.abs(topView.getHeight() - getScrollY());
    } else {
      distance = Math.abs(topView.getHeight() - (topView.getHeight() - getScrollY()));
    }

    final int duration;
    velocityY = Math.abs(velocityY);
    if (velocityY > 0) {
      duration = 3 * Math.round(1000 * (distance / velocityY));
    } else {
      final float distanceRatio = (float) distance / getHeight();
      duration = (int) ((distanceRatio + 1) * 150);
    }

    return duration;

  }

  private void animateScroll(float velocityY, final int duration, boolean consumed) {
    final int currentOffset = getScrollY();
    final int topHeight = topView.getHeight();
    if (mOffsetAnimator == null) {
      mOffsetAnimator = new ValueAnimator();
      mOffsetAnimator.setInterpolator(new LinearInterpolator());
      mOffsetAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
          if (animation.getAnimatedValue() instanceof Integer) {
            scrollTo(0, (Integer) animation.getAnimatedValue());
          }
        }
      });
    } else {
      mOffsetAnimator.cancel();
    }
    mOffsetAnimator.setDuration(Math.min(duration, 600));

    if (velocityY >= 0) {
      mOffsetAnimator.setIntValues(currentOffset, topHeight);
      mOffsetAnimator.start();
    } else {
      //如果子View没有消耗down事件 那么就让自身滑倒0位置
      if (!consumed) {
        mOffsetAnimator.setIntValues(currentOffset, 0);
        mOffsetAnimator.start();
      }

    }
  }



  private static final int INVALID_POINTER = -1;

  private Runnable mFlingRunnable;

  private boolean mIsBeingDragged;
  private int mActivePointerId = INVALID_POINTER;
  private int mLastMotionY;
  private int mTouchSlop = -1;
  private VelocityTracker mVelocityTracker;
  private Rect rect = new Rect();

  @Override
  public boolean onInterceptTouchEvent(MotionEvent ev) {
    if (mTouchSlop < 0) {
      mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
    }

    View child = topView;

    final int action = ev.getAction();

    // Shortcut since we're being dragged
    if (action == MotionEvent.ACTION_MOVE && mIsBeingDragged) {
      return true;
    }

    switch (ev.getActionMasked()) {
      case MotionEvent.ACTION_DOWN: {

        Log.e(TAG, "onInterceptTouchEvent --> down");

        if (mFlingRunnable != null) {
          removeCallbacks(mFlingRunnable);
          mFlingRunnable = null;
        }


        mIsBeingDragged = false;
        final int x = (int) ev.getX();
        final int y = (int) ev.getY();

        Log.e(TAG, "onInterceptTouchEvent -->isPointInChildBounds  "+isPointInChildBounds(child, x, y));

        if (isPointInChildBounds(child, x, y)) {
          mLastMotionY = y;
          mActivePointerId = ev.getPointerId(0);
          ensureVelocityTracker();
        }
        break;
      }

      case MotionEvent.ACTION_MOVE: {
        final int activePointerId = mActivePointerId;
        if (activePointerId == INVALID_POINTER) {
          // If we don't have a valid id, the touch down wasn't on content.
          break;
        }
        final int pointerIndex = ev.findPointerIndex(activePointerId);
        if (pointerIndex == -1) {
          break;
        }

        final int y = (int) ev.getY(pointerIndex);
        final int yDiff = Math.abs(y - mLastMotionY);
        if (yDiff > mTouchSlop) {
          mIsBeingDragged = true;
          mLastMotionY = y;
        }
        break;
      }

      case MotionEvent.ACTION_CANCEL:
      case MotionEvent.ACTION_UP: {

        Log.e(TAG, "onInterceptTouchEvent --> up");

        mIsBeingDragged = false;
        mActivePointerId = INVALID_POINTER;
        if (mVelocityTracker != null) {
          mVelocityTracker.recycle();
          mVelocityTracker = null;
        }
        break;
      }
    }

    if (mVelocityTracker != null) {
      mVelocityTracker.addMovement(ev);
    }

    return mIsBeingDragged;
  }

  @Override
  public boolean onTouchEvent(MotionEvent ev) {

    if (mTouchSlop < 0) {
      mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
    }

    switch (ev.getActionMasked()) {
      case MotionEvent.ACTION_DOWN: {
        Log.e(TAG, "onTouchEvent --> down");
        final int x = (int) ev.getX();
        final int y = (int) ev.getY();

        if (isPointInChildBounds(scrollableView, x, y) ) {
          mLastMotionY = y;
          mActivePointerId = ev.getPointerId(0);
          ensureVelocityTracker();
        } else {
          return false;
        }

      }

      case MotionEvent.ACTION_MOVE: {
        final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
        if (activePointerIndex == -1) {
          return false;
        }

        final int y = (int) ev.getY(activePointerIndex);
        int dy = mLastMotionY - y;

        if (!mIsBeingDragged && Math.abs(dy) > mTouchSlop) {
          mIsBeingDragged = true;
          if (dy > 0) {
            dy -= mTouchSlop;
          } else {
            dy += mTouchSlop;
          }
        }

        if (mIsBeingDragged) {
          mLastMotionY = y;
          // We're being dragged so scroll the ABL
          scroll(topView, dy, getMaxDragOffset(topView), 0);
        }
        break;
      }

      case MotionEvent.ACTION_UP:
        Log.e(TAG, "onTouchEvent --> up");
        if (mVelocityTracker != null) {
          mVelocityTracker.addMovement(ev);
          mVelocityTracker.computeCurrentVelocity(1000);
          float yvel = mVelocityTracker.getYVelocity(mActivePointerId);
          //fling(parent, child, -getScrollRangeForDragFling(child), 0, yvel);
        }
        // $FALLTHROUGH
      case MotionEvent.ACTION_CANCEL: {
        mIsBeingDragged = false;
        mActivePointerId = INVALID_POINTER;
        if (mVelocityTracker != null) {
          mVelocityTracker.recycle();
          mVelocityTracker = null;
        }
        break;
      }
    }

    if (mVelocityTracker != null) {
      mVelocityTracker.addMovement(ev);
    }

    return true;


  }

  private boolean isPointInChildBounds(View child,int x, int y){
    rect.setEmpty();
    rect.set(0,0,child.getWidth(),child.getHeight());
    //ViewGroupUtils.getDescendantRect(this,child,rect);
    return rect.contains(x, y);
  }
  private int getMaxDragOffset(View view) {
    return -view.getHeight();
  }

  private int getScrollRangeForDragFling(View view) {
    return view.getHeight();
  }


  private int scroll(View header,
      int dy, int minOffset, int maxOffset) {


    mScroller.startScroll(getScrollX(),getScrollY(),0,dy);
    //scrollBy(0,dy);

    Log.e(TAG, "scroll --> "+dy+"   "+minOffset+"   "+maxOffset);

    return 0;

//    return setHeaderTopBottomOffset(header,
//        getTopBottomOffsetForScrollingSibling() - dy, minOffset, maxOffset);
  }

//  int setHeaderTopBottomOffset(View header, int newOffset,
//      int minOffset, int maxOffset) {
//    final int curOffset = getTopAndBottomOffset();
//    int consumed = 0;
//
//    if (minOffset != 0 && curOffset >= minOffset && curOffset <= maxOffset) {
//      // If we have some scrolling range, and we're currently within the min and max
//      // offsets, calculate a new offset
//      newOffset = MathUtils.clamp(newOffset, minOffset, maxOffset);
//
//      if (curOffset != newOffset) {
//        setTopAndBottomOffset(newOffset);
//        // Update how much dy we have consumed
//        consumed = curOffset - newOffset;
//      }
//    }
//
//    return consumed;
//  }
//
//  private ViewOffsetHelper mViewOffsetHelper;
//
//  private int getTopBottomOffsetForScrollingSibling() {
//    return getTopAndBottomOffset();
//  }

  private void ensureVelocityTracker() {
    if (mVelocityTracker == null) {
      mVelocityTracker = VelocityTracker.obtain();
    }
  }

}
